// OpentTxl-C Version 11
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// The main TXL process, used to run the stages of a TXL transformation:
//   - bootstrap of the TXL language grammar to a grammar tree, 
//   - scan and parse of the TXL program source,
//   - compile of the input language grammar to a grammar tree, 
//   - compile of the user rules to a rule table,
//   - scan and parse of the input source, 
//   - application of the transformation rules.

// Modification Log

// v11.0 Initial revision, adapted from OpenTxl 11.0

// v11.1 Added anonymous conditions, e.g., where _ [test]
//       Added optional match/replace rules, implicit match [any]
//       Added new predefined function [faccess FILE MODE] 
//       Added NBSP (ASCII 160) as space character and separator
//       Fixed local variable binding bug issue #1

// v11.2 Corrected Unicode conflict with Latin-1 character set
//       Added shallow extract [^/]
//       Changed default message level to quiet
//       Changed stack limit message to when verbose only

// v11.3 Fixed bug in output of zero-length tokens
//       Added multiple skipping criteria
//       Fixed lookahead source line number bug
//       Fixed multiple nl-comments source line number bug
//       Fixed compatibility of [srclinenumber] with [number]
//       Updated default size to -s 128 to avoid auditor garbage recoveries
//       Removed hidden uses of strcpy, memcpy, memset, etc.
//       Added time stamps to phase messages
//       Added support for embedded TXL in C
//       Fixed serious bug in skipping rules
//       Fixed problems with single user installation

#define TXLMAIN

// I/O, strings, memory allocation, exception handling
#include "support.h"

// Localization
#include "locale.h"

// Limits
#include "limits.h"

// Global modules
#include "options.h"
#include "charset.h"
#include "tokens.h"
#include "errors.h"
#include "trees.h"
#include "idents.h"
#include "shared.h"
#include "symbols.h"
#include "rules.h"

// Phases of the process
#include "boot.h"
#include "scan.h"
#include "parse.h"
#include "unparse.h"
#ifndef NOCOMPILE
    #include "compdef.h"
    #include "comprul.h"
#endif
#include "xform.h"
#ifndef NOLOADSTORE
    // loadstor.i
    #include "loadstor.h"
#endif

// Target grammar tree
treePT inputGrammarTreeTP;

void TProg (void) {
    // Initialize all modules
    phase = INITIALIZE;

    locale ();
    charset ();
    errors ();
    options ();
    limits ();
    tokens ();
    tree ();
    ident ();
    shared ();
    symbol ();
    rule ();
    scanner ();
    unparser ();
    parser ();
#ifndef NOCOMPILE
    defineCompiler ();
    ruleCompiler ();
#endif
    transformer ();
#ifndef NOLOADSTORE
    loadstore ();
#endif

    // Announce us
    if (!(options_option[quiet_p])) {
        fprintf (stderr, "%s\n", version);
    }

    // Keep track of tree space use by each phase
    int oldTreeCount = 0;
    int oldKidCount = 0;

    // Phase 1. Compile the TXL program

    phase = COMPILE;

    #ifndef NOLOADSTORE 
    if (options_option[load_p]) {
        if (!(options_option[quiet_p])) {
            fprintf (stderr, "[%s] ", time_precisetime());
            fprintf (stderr, "Loading %s ...\n", options_txlCompiledFileName);
        }

        loadstore_restore (options_txlCompiledFileName);

        if (options_option[verbose_p]) {
            fprintf (stderr, "  ... used %d trees and %d kids.\n", tree_treeCount, tree_kidCount);
        }

    } else { 
    #endif

        #ifndef NOCOMPILE 
        {
            // Step 1. Make the TXL language grammar tree
            treePT txlGrammarTreeTP = nilTree;

            bootstrap_makeGrammarTree (&(txlGrammarTreeTP));

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Bootstrapping TXL ... ");
                fprintf (stderr, "  ... used %d trees and %d kids.\n", tree_treeCount, tree_kidCount);
            }

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // Step 2. Parse the TXL program using the TXL grammar tree
            treePT txlSourceParseTreeTP = nilTree;

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "Scanning the TXL program %s\n", options_txlSourceFileName);
            } else if (!(options_option[quiet_p])) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "Compiling %s ...\n", options_txlSourceFileName);
            }

            scanner_tokenize (options_txlSourceFileName, true, true);

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Parsing the TXL program");
            }

            bool save_tree_print_p = options_option[tree_print_p];
            options_option[tree_print_p] = false;

            parser_initializeParse ("", false, false, true, (struct ruleLocalsT *) 0, (parser_parseVarOrExpProc *) 0);
            parser_parse (txlGrammarTreeTP, &(txlSourceParseTreeTP));

            options_option[tree_print_p] = save_tree_print_p;

            if (txlSourceParseTreeTP == nilTree) {
                // unsuccessful parse of TXL program
                syntaxError (failTokenIndex);
            }

            if (options_option[boot_parse_p]) {
                fprintf (stderr, "%s\n", "----- TXL Program Parse Tree -----");
                unparser_printParse (txlSourceParseTreeTP, 0, 0);
                fprintf (stderr, "%s\n", "----- End TXL Program Parse Tree -----");
            }

            if (options_option[verbose_p]) {
                fprintf (stderr, "  ... used %d trees and %d kids.\n", (tree_treeCount - oldTreeCount), (tree_kidCount - oldKidCount));
            }

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // mark beginning of user program tree space, for load/store compression
            tree_beginUserTreeSpace ();

            // Step 3. Make the nonterminal symbol table from the defines of the user's TXL program 

            // We save the symbol table from the input language grammar, which contains all the 
            // definitions of the nonterminals (defined by the TXL program) of the input language.
            // These are not needed right away: when the input language source is parsed, 
            // only the top level define [program] is needed.  

            // Later (in phase two) when the rules are being compiled into a rule table, 
            // the patterns and replacements are parsed from strings of tokens to "parse" trees
            // for their nonterminal symbol types, including variables and expressions as appropriate.

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Making the input language grammar tree");
            }

            defineCompiler_makeGrammarTree (txlSourceParseTreeTP, &(inputGrammarTreeTP));

            if (options_option[verbose_p]) {
                fprintf (stderr, "  ... used %d trees and %d kids.\n", (tree_treeCount - oldTreeCount), (tree_kidCount - oldKidCount));
            }

            if (options_option[grammar_print_p]) {
                fprintf (stderr, "%s\n", "----- Grammar Tree -----");
                unparser_printGrammar (inputGrammarTreeTP, 0);
                fprintf (stderr, "%s\n", "----- End Grammar Tree -----");
            }

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // Step 4. Make the rule table from the rules of the user's TXL program
            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Making the rule table");
            }

            ruleCompiler_makeRuleTable (txlSourceParseTreeTP);

            if (options_option[verbose_p]) {
                fprintf (stderr, "  ... used %d trees and %d kids.\n", (tree_treeCount - oldTreeCount), (tree_kidCount - oldKidCount));
            }
        }
        #endif

    #ifndef NOLOADSTORE
    }
    #endif

    #ifndef NOLOADSTORE 
    if (options_option[compile_p]) {

        #ifndef NOCOMPILE
        // Store the compiled TXL bytecode for the user's nonterminal symbol table and rules
        if (!(options_option[quiet_p])) {
            fprintf (stderr, "[%s] ", time_precisetime());
            fprintf (stderr, "Storing %s ...\n", options_txlCompiledFileName);
        }

        loadstore_save (options_txlCompiledFileName);

        if (options_option[verbose_p]) {
            fprintf (stderr, "  ... a total of %d trees and %d kids.\n", tree_treeCount, tree_kidCount);
        }

        if (!(options_option[quiet_p])) {
            fprintf (stderr, "[%s] ", time_precisetime());
            fprintf (stderr, "%s\n", "Done.");
        }
        #endif

    } else { 
    #endif

        // Phase 2. Run the compiled TXL program 
        {
            phase = TRANSFORM;

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // Step 5. Parse the input language source using the user's [program] nonterminal grammar tree
            treePT inputParseTreeTP = nilTree;

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "Scanning the input file %s\n", options_inputSourceFileName);
            } else if (!(options_option[quiet_p])) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "Parsing %s ...\n", options_inputSourceFileName);
            }

            scanner_tokenize (options_inputSourceFileName, true, false);

            int inputLastTokenIndex = lastTokenIndex;

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Parsing the input file");
            }

            string context;
            stringprintf (context, "input file '%s'", options_inputSourceFileName);

            parser_initializeParse (context, true, false, false, (struct ruleLocalsT *) 0, (* (parser_parseVarOrExpProc *) 0));
            parser_parse (inputGrammarTreeTP, &(inputParseTreeTP));

            if (inputParseTreeTP == nilTree) {
                // unsuccessful parse
                syntaxError (failTokenIndex);
            }

            if (options_option[parse_print_p]) {
                fprintf (stderr, "%s\n", "----- Input Parse Tree -----");
                unparser_printParse (inputParseTreeTP, 0, 0);
                fprintf (stderr, "%s\n", "----- End Input Parse Tree -----");
            }

            if (options_option[verbose_p]) {
                fprintf (stderr, "  ... used %d trees and %d kids.\n", (tree_treeCount - oldTreeCount), (tree_kidCount - oldKidCount));
            }

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // Step 6. Apply the rules to transform the parsed input source
            treePT transformedInputParseTreeTP;

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Applying the transformation rules");
            } else if (!(options_option[quiet_p])) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Transforming ...");
            }

            transformer_applyMainRule (inputParseTreeTP, &(transformedInputParseTreeTP));

            if (options_option[result_tree_print_p]) {
                fprintf (stderr, "%s\n", "----- Output Parse Tree -----");
                unparser_printParse (transformedInputParseTreeTP, 0, 0);
                fprintf (stderr, "%s\n", "----- End Output Parse Tree -----");
            }

            if (options_option[verbose_p]) {
                fprintf (stderr, "  ... used %d trees and %d kids.\n", (tree_treeCount - oldTreeCount), (tree_kidCount - oldKidCount));
            }

            oldTreeCount = tree_treeCount;
            oldKidCount = tree_kidCount;

            // Step 7. Generate the transformed input language source

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Generating transformed output");
            }

            if (stringcmp (options_outputSourceFileName, "") != 0) {
                int outputStream = 0;
                tfopen (OPEN_CHAR_WRITE, options_outputSourceFileName, &outputStream);

                if (outputStream == 0) {
                    string message;
                    stringprintf (message, "Unable to open output file '%s'", options_outputSourceFileName);
                    error ("", message, FATAL, 991);
                }

                if (options_option[xmlout_p]) {
                    unparser_printParse (transformedInputParseTreeTP, outputStream, 0);
                } else {
                    unparser_printLeaves (transformedInputParseTreeTP, outputStream, true);
                }

                tfclose (outputStream);

            } else {
                if (options_option[xmlout_p]) {
                    unparser_printParse (transformedInputParseTreeTP, tfstdout, 0);
                } else {
                    unparser_printLeaves (transformedInputParseTreeTP, tfstdout, true);
                }
            }

            // Optionally output statistics 
            if (options_option[verbose_p]) {
                fprintf (stderr, "Used a total of %d trees (%d%%) and %d kids (%d%%).\n", tree_treeCount, 
                    (tree_treeCount * 100) / maxTrees, tree_kidCount, (tree_kidCount * 100) / maxKids);
            }

            if (options_option[usage_p]) {
                fprintf (stderr, "===== TXL Resource Usage Summary =====\n");
                fprintf (stderr, "Defines             ");
                fprintf (stderr, "%d/%d\n", symbol_nSymbols, maxSymbols);
                fprintf (stderr, "Rules/functions     ");
                fprintf (stderr, "%d/%d\n", rule_nRules, maxRules);
                fprintf (stderr, "Keywords            ");
                fprintf (stderr, "%d/%d\n", scanner_nKeys, maxKeys);
                fprintf (stderr, "Compound tokens     ");
                fprintf (stderr, "%d/%d\n", scanner_nCompounds, maxCompoundTokens);
                fprintf (stderr, "Comment tokens      ");
                fprintf (stderr, "%d/%d\n", scanner_nComments, maxCommentTokens);
                fprintf (stderr, "Token patterns      ");
                fprintf (stderr, "%d/%d\n", scanner_nPatterns, maxPatternTokens);
                fprintf (stderr, "Ident/string tokens ");
                fprintf (stderr, "%d/%d\n", ident_nIdents, maxIdents);
                fprintf (stderr, "Ident/string chars  ");
                fprintf (stderr, "%d/%d\n", ident_nIdentChars, maxIdentChars);
                fprintf (stderr, "Input size (tokens) ");
                fprintf (stderr, "%d/%d\n", inputLastTokenIndex, maxTokens);
                fprintf (stderr, "Input files         ");
                fprintf (stderr, "%d/%d\n", nFiles, maxFiles);
                fprintf (stderr, "Trees               ");
                fprintf (stderr, "%d/%d\n", tree_treeCount, maxTrees);
                fprintf (stderr, "Kids                ");
                fprintf (stderr, "%d/%d\n", tree_kidCount, maxKids);
                fprintf (stderr, "=== ===\n");
            }

            // Detailed stats for use in tuning the TXL processor's memory footprint
            #ifdef SPACETUNING 
            type dummyaddrint : addressint;
            type dummyint : int;

            int szTrees := sizeof (tree_trees);
            int szKids := sizeof (tree_kids);
            int szIdents := sizeof (ident_idents);
            int szIdtext := sizeof (ident_identText);
            int szIdtrees := sizeof (ident_identTree);
            int szTokens := sizeof (inputTokens);
            int szTokenkind := sizeof (inputTokenKind);
            int szTokenline := sizeof (inputTokenLineNum);
            int szTokentree := sizeof (inputTokenTP);
            int szSymbols := sizeof (symbol_symbolTable);
            int szRules := sizeof (rule_rules);
            int szStack := maxStackUse;

            fprintf (stderr, "\n=== TXL Memory Usage Summary ===\n");
            fprintf (stderr, "Trees      %d\n", szTrees);
            fprintf (stderr, "Kids       %d\n", szKids);
            fprintf (stderr, "Idents     %d\n", szIdents);
            fprintf (stderr, "Idtext     %d\n", szIdtext);
            fprintf (stderr, "Idtrees    %d\n", szIdtrees);
            fprintf (stderr, "Tokens     %d\n", szTokens);
            fprintf (stderr, "Tokenkind  %d\n", szTokenkind);
            fprintf (stderr, "Tokenline  %d\n", szTokenline);
            fprintf (stderr, "Tokentree  %d\n", szTokentree);
            fprintf (stderr, "Symbols    %d\n", szSymbols);
            fprintf (stderr, "Rules      %d\n", szRules);
            fprintf (stderr, "Stack      %d\n", szStack);
            fprintf (stderr, skip, "Total %d\n", szTrees + szKids + szIdents  + szIdtext + szIdtrees
                + szTokens + szTokenkind + szTokenline + szTokentree + szSymbols
                + szRules + szStack);
            fprintf (stderr, "=== ===\n\n");
            #endif

            if (options_option[verbose_p]) {
                fprintf (stderr, "[%s] ", time_precisetime());
                fprintf (stderr, "%s\n", "Done.");
            }

            if (exitcode != 0) {
                throw (exitcode);
            }
        }
#ifndef NOLOADSTORE
    }
#endif
}
